using System;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;
using CodePlex.TfsLibrary.ClientEngine;
using CodePlex.TfsLibrary.ObjectModel;
using CodePlex.TfsLibrary.RepositoryWebSvc;

namespace CodePlex.CodePlexClientLibrary
{
    public class StatusViewPresenter
    {
        readonly bool connectToServer;
        readonly string diffArgs;
        readonly string diffTool;
        readonly ITfsEngine engine;
        bool itemsShown;
        readonly string mergeArgs;
        readonly string mergeTool;
        readonly bool recursive;
        readonly bool verbose;
        readonly IStatusView view;

        public StatusViewPresenter(IStatusView view,
                                   ITfsEngine engine,
                                   bool verbose,
                                   bool recursive,
                                   bool connectToServer)
        {
            this.view = view;
            this.engine = engine;
            this.verbose = verbose;
            this.recursive = recursive;
            this.connectToServer = connectToServer;
        }

        public StatusViewPresenter(IStatusView view,
                                   ITfsEngine engine,
                                   bool verbose,
                                   bool recursive,
                                   bool connectToServer,
                                   string diffTool,
                                   string diffArgs,
                                   string mergeTool,
                                   string mergeArgs)
            : this(view, engine, verbose, recursive, connectToServer)
        {
            this.diffTool = diffTool;
            this.diffArgs = diffArgs == null ? null : diffArgs
                                                          .Replace("{basepath}", @"""{0}""")
                                                          .Replace("{mypath}", @"""{1}""")
                                                          .Replace("{basename}", @"""{2} : base copy""")
                                                          .Replace("{myname}", @"""{2} : working copy""");
            this.mergeTool = mergeTool;
            this.mergeArgs = mergeArgs == null ? null : mergeArgs
                                                            .Replace("{basepath}", @"""{0}""")
                                                            .Replace("{mypath}", @"""{1}""")
                                                            .Replace("{theirpath}", @"""{2}""")
                                                            .Replace("{mergepath}", @"""{3}""")
                                                            .Replace("{basename}", @"""{4} : base copy""")
                                                            .Replace("{myname}", @"""{4} : working copy""")
                                                            .Replace("{theirname}", @"""{4} : revision {5}""")
                                                            .Replace("{mergename}", @"""{4}""");
        }

        public void DoAdd(SourceItem item)
        {
            engine.Add(item.LocalName, false, delegate(SourceItem i,
                                                       SourceItemResult result)
                                              {
                                                  ProcessSourceItem(item.LocalName, result, true);
                                              });
        }

        public void DoDelete(SourceItem item)
        {
            engine.Delete(item.LocalName, true, delegate(SourceItem i,
                                                         SourceItemResult result)
                                                {
                                                    ProcessSourceItem(item.LocalName, result, true);
                                                });
        }

        public void DoDiff(SourceItem item)
        {
            LaunchDiff(item.LocalTextBaseName, item.LocalName);
        }

        public void DoDiffMineAgainstBase(SourceItem item)
        {
            LaunchDiff(item.LocalName + ".mine", item.LocalTextBaseName);
        }

        public void DoDiffTheirsAgainstBase(SourceItem item)
        {
            LaunchDiff(item.LocalConflictTextBaseName, item.LocalTextBaseName);
        }

        public void DoMerge(SourceItem item)
        {
            LaunchMerge(item.LocalTextBaseName, item.LocalConflictTextBaseName, item.LocalName + ".mine", item.LocalName, item.LocalConflictChangesetId);
        }

        public void DoResolve(SourceItem item)
        {
            engine.Resolve(item.LocalName, false, delegate(SourceItem i,
                                                           SourceItemResult result)
                                                  {
                                                      ProcessSourceItem(item.LocalName, result, true);
                                                  });
        }

        public void DoRevert(SourceItem item)
        {
            engine.Revert(item.LocalName, true, delegate(SourceItem i,
                                                         SourceItemResult result)
                                                {
                                                    ProcessSourceItem(item.LocalName, result, true);
                                                });
        }

        void LaunchDiff(string baseFilename,
                        string mineFilename)
        {
            if (diffTool == null || diffArgs == null)
            {
                if (MessageBox.Show("There is no diff tool configured. Would you like information on how to configure one?", "Query", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
                    Process.Start("http://www.codeplex.com/CodePlexClient/Wiki/View.aspx?title=ConfigFile#DiffAndMerge");

                return;
            }

            string filename = Path.GetFileName(mineFilename);
            CodePlexClientUtil.StartProcess(diffTool, string.Format(diffArgs, baseFilename, mineFilename, filename));
        }

        void LaunchMerge(string baseFilename,
                         string theirsFilename,
                         string mineFilename,
                         string mergeFilename,
                         int conflictChangesetId)
        {
            if (mergeTool == null || mergeArgs == null)
            {
                if (MessageBox.Show("There is no merge tool configured. Would you like information on how to configure one?", "Query", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
                    Process.Start("http://www.codeplex.com/CodePlexClient/Wiki/View.aspx?title=ConfigFile#DiffAndMerge");

                return;
            }

            DateTime originalDateTime = File.GetLastWriteTimeUtc(mergeFilename);
            string filenamePortion = Path.GetFileName(mergeFilename);
            string args = string.Format(mergeArgs, baseFilename, mineFilename, theirsFilename, mergeFilename, filenamePortion, conflictChangesetId);

            CodePlexClientUtil.StartProcess(mergeTool, args).WaitForExit();

            if (originalDateTime != File.GetLastWriteTimeUtc(mergeFilename))
                if (view.QueryForResolve(filenamePortion))
                    engine.Resolve(mergeFilename, false, delegate(SourceItem i,
                                                                  SourceItemResult result)
                                                         {
                                                             ProcessSourceItem(mergeFilename, result, true);
                                                         });
        }

        void ProcessSourceItem(string localPath,
                               SourceItemResult result,
                               bool isUpdate)
        {
            if (result != SourceItemResult.S_Ok && result != SourceItemResult.S_ForcedDelete)
            {
                itemsShown = true;
                view.DisplayError(localPath, result);
                return;
            }

            SourceItem item = null;

            try
            {
                item = engine.GetSourceItem(localPath);
            }
            catch (TfsStateException) {}

            if (item != null && ShouldShowItem(item))
            {
                itemsShown = true;
                view.DisplayItem(item);

                if (recursive && isUpdate && item.ItemType == ItemType.Folder && item.LocalItemStatus != SourceItemStatus.Unversioned)
                    engine.Status(item.LocalName, VersionSpec.Latest, false, connectToServer, delegate(SourceItem innerItem,
                                                                                                       SourceItemResult innerResult)
                                                                                              {
                                                                                                  ProcessSourceItem(innerItem.LocalName, innerResult, true);
                                                                                              });
            }
            else
                view.RemoveItem(localPath);
        }

        public virtual bool Run(string localPath)
        {
            engine.Status(localPath, VersionSpec.Latest, true, connectToServer, delegate(SourceItem item,
                                                                                         SourceItemResult result)
                                                                                {
                                                                                    ProcessSourceItem(item.LocalName, result, false);
                                                                                });

            if (itemsShown)
                return view.ShowView();

            view.DisplayNoItemsMessage();
            return false;
        }

        protected virtual bool ShouldShowItem(SourceItem item)
        {
            return verbose || item.LocalItemStatus != SourceItemStatus.Unmodified;
        }
    }
}